'''
# Python 正则表达式笔记

## 1. 基础
- **模块**：`import re`
- **核心函数**：
  - `re.match(pattern, s)`：只从开头匹配
  - `re.search(pattern, s)`：扫描整个字符串，返回首个匹配
  - `re.findall(pattern, s)`：返回所有**非重叠**匹配列表
  - `re.finditer(pattern, s)`：返回迭代器，元素是`Match`对象
  - `re.sub(pattern, repl, s, count=0)`：替换
  - `re.split(pattern, s)`：分割
- **预编译**：`pat = re.compile(r'...')`，此后`pat.search(s)`等，提高效率

## 2. 常用单字符匹配
| 语法 | 含义 |
|------|------|
| `.` | 任意字符（除换行，可用`re.DOTALL`打开） |
| `\d` | 数字 ≡ `[0-9]` |
| `\D` | 非数字 ≡ `[^0-9]` |
| `\w` | 单词字符 ≡ `[a-zA-Z0-9_]` |
| `\W` | 非单词字符 |
| `\s` | 空白 ≡ `[ \t\n\r\f\v]` |
| `\S` | 非空白 |

## 3. 锚点/零宽断言
| 语法 | 含义 |
|------|------|
| `^` | 行首（`re.MULTILINE`下可匹配每行） |
| `$` | 行尾 |
| `\b` | 单词边界 |
| `\B` | 非单词边界 |
| `(?=exp)` | 正向前瞻 |
| `(?!exp)` | 负向前瞻 |
| `(?<=exp)` | 正向后顾（定宽） |
| `(?<!exp)` | 负向后顾 |

## 4. 重复/贪婪与懒惰
| 语法 | 含义 |
|------|------|
| `*` | 0次或更多（贪婪） |
| `+` | 1次或更多（贪婪） |
| `?` | 0或1次（贪婪） |
| `{m,n}` | m到n次（贪婪） |
| 在上表后加`?` → 懒惰版本，如`+?`、`{m,n}?` |

## 5. 分组与捕获
| 语法 | 含义 |
|------|------|
| `(exp)` | 捕获分组，编号从1开始 |
| `(?P<name>exp)` | 命名分组 |
| `(?:exp)` | 非捕获分组 |
| `\1` `\2` … | 反向引用（在pattern里） |
| `match.group(1)` | 获取捕获内容 |
| `match.group('name')` | 命名分组获取 |

## 6. 标志位（常用）
```python
re.I  # 忽略大小写
re.M  # 多行^$
re.S  # .匹配换行
re.X  # 扩展写法，可写注释/空白
```
可组合：`re.I | re.M`

## 7. Match对象常用方法
- `.group()` / `.group(1)` / `.group('name')`
- `.groups()`：所有捕获元组
- `.start()/.end()`：起止索引
- `.span()`：`(start, end)`

## 8. 替换与分割进阶
- `re.sub(pattern, repl, s, count=0)`
  - `repl`可以是函数：`def func(match): return ...`
- `re.split(pattern, s, maxsplit=0)`：保留分隔组可用`()`包起来

## 9. 实战小抄
- **邮箱（简易）**：
  ```python
  pat = re.compile(r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
  ```
- **国内手机号**：
  ```python
  pat = re.compile(r'1[3-9]\d{9}')
  ```
- **IPv4**：
  ```python
  pat = re.compile(r'\b(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}\b')
  ```
- **提取markdown图片**：
  ```python
  pat = re.compile(r'!\[.*?\]\((.*?)\)')
  ```

## 10. 调试技巧
- 在线工具：[regex101.com](https://regex101.com) → 选Flavor **PCRE**
- Python端打印：`print(repr(match.group()))`看清换行/空白
- 使用`re.X`写多行注释：
  ```python
  pat = re.compile(r
      [A-Za-z]+   # 字母
      \d+         # 数字
  , re.X)


## 11. 常见坑
- **字符串转义**：Python字符串里先转义一次→正则引擎再转一次，尽量用**原始字符串**`r'...'`
- **贪婪吃光一切**：`.+(zip)`会把最后一个zip前的全吃掉，改用`.+?`
- **零宽后顾必须定宽**：`(?<=ab.*)`❌ → 可用`regex`模块（`pip install regex`）支持变宽后顾
- **findall与分组**：若有捕获组，`findall`返回**组元组列表**而非整体

## 12. 高级（可选）
- **regex模块**：`import regex`（兼容`re`并支持变宽后顾、Unicode属性`\p{Han}`等）
- **Unicode属性**：
  ```python
  regex.findall(r'\p{Han}+', text)    # 所有汉字
  ```

---

> 一句话记忆：
> **先编译，用原始串，分组命名好维护，前瞻后顾灵活定边界，贪婪懒惰看量词，换行多行靠标志。**

祝你正则写到最后，一次编译，终身受益！
'''
